﻿//DSN Export-o-Matic 3000 - Exports ODBC DSN settings to .reg files
//Copyright (C) 2010 David Helps

//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

using System;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.Win32;
using System.Management;


namespace DSNExport
{
    public partial class frmDSNExport : Form
    {
        // public variables
        
        // shortcut to line breaks for text files
        string strNL = Environment.NewLine;

        // specify the Registry key in which ODBC DSNs are stored
        const string strODBCRegKey = @"Software\\ODBC\\ODBC.INI\\";

        // specify the Registry key in which ODBC DSNs drivers are stored
        const string strODBCDriverRegKey = @"Software\\ODBC\\ODBC.INI\\ODBC Data Sources";

        // the application settings
        Properties.Settings objUserSettings;

        // public methods

        private void SetDefaultExportLocation(object sender, EventArgs e)
        {
            // stores a value in the application settings to save the default export location

            // start with the current setting selected
            dlgDefaultExportLocation.SelectedPath = objUserSettings.DefaultExportLocation.ToString();
            if (dlgDefaultExportLocation.ShowDialog() == DialogResult.OK)
            {
                // save the selected path into the application settings
                objUserSettings.DefaultExportLocation = dlgDefaultExportLocation.SelectedPath.ToString();

                // save the user settings
                objUserSettings.Save();
            }
        }

        private RegistryKey ConnectToRegistry(string strRegHive)
        {
           // connect to the given Registry node
           switch (strRegHive)
            {
               case "HKLM":
                  return Registry.LocalMachine;
               case "HKCU":
                  return Registry.CurrentUser;
               default:
                  return Registry.LocalMachine;
            }
        }

        private ArrayList GetDSNList(RegistryKey objRegistry)
        {
            // get the list of DSNs from the given Registry location
            
            // for building the list of DSNs
            ArrayList arrDSNList = new ArrayList();

            // check for DSN entry subkeys
            if (objRegistry.OpenSubKey(strODBCRegKey).SubKeyCount > 0)
            {

                // enumerate the relevant subkeys into the return array
                foreach (string strSubKeyName in objRegistry.OpenSubKey(strODBCRegKey).GetSubKeyNames())
                {
                    // ignore the ODBC Data subkey
                    if (strSubKeyName != "ODBC Data Sources")
                    {
                        arrDSNList.Add(strSubKeyName);
                    }
                }
            }

            // if no DSNs were found, add a null entry to the array
            if (arrDSNList.Count == 0)
            {
                arrDSNList.Add("None found");
            }
            
            // return the array we have built
            return arrDSNList;
        }

        private string GetDSNRegHive(string strDSNListEntry)
        {
            // determines the Registry hive from a DSN List entry
            
            // Determine which Registry hive the selected DSN is in
            switch (strDSNListEntry.Contains("[System DSN]")|strDSNListEntry.Contains("[User DSN]"))
            {
                case true:
                    if (strDSNListEntry.Contains("[System DSN]"))
                    {
                        return "HKLM";
                    }
                    else
                    {
                        return "HKCU";
                    }
                case false:
                    return "Error";
                default:
                    return "Error";
            }
        }

        private string BuildExportString(string strDSN, string strRegHive)
        {
            // builds a string for writing a .reg file for a given DSN
            // built from the DSN's Registry key (and subkeys) and the driver entry

            // the header text is required for .reg files
            string strHeader = "Windows Registry Editor Version 5.00" + strNL + strNL;
                        
            // create a Registry object for the specified Registry hive
            RegistryKey objRegistry = ConnectToRegistry(strRegHive);

            // call the function to get the data from the given DSN's registry key
            string strDSNKey = GetRegData(strDSN, objRegistry);

            // build the text for the ODBC driver key name
            string strDSNDriver = "[" + objRegistry.Name + @"\" + strODBCDriverRegKey.Replace(@"\\", @"\") + "]" +strNL;
            
            // get the DSN's driver entry from the ODBC Data Sources subkey
            RegistryKey objRegKey = ConnectToRegistry(strRegHive);
            objRegKey = objRegKey.OpenSubKey(strODBCDriverRegKey, false);
            strDSNDriver = strDSNDriver + @"""" + strDSN + @"""" + "=" + @"""" + objRegKey.GetValue(strDSN).ToString() + @"""" + strNL;
            
            //assemble the string
            return strHeader + strDSNKey + strDSNDriver;
        }

        private string GetRegData(string strRegKey, RegistryKey objRegistry)
        {
            // returns a string of all data in a given Registry key and subkeys
            
            // determine the first part of the Registry key path from the hive
            string strHive = objRegistry.ToString();

            // put the full Registry key path in to the string, replace the double \\ with single \
            string strRegData = "[" + strHive + @"\" 
                                + strODBCRegKey.Replace(@"\\",@"\") 
                                + strRegKey.Replace(@"\\", @"\") + "]" 
                                + strNL;

            // get the list of mapped drives
            ArrayList arrMappedDrives = GetMappedDrives();
            bool ynMappedDrives = arrMappedDrives[0].ToString()!="None Found";
  
            // open the specified Registry key from the specified hive
            RegistryKey objRegKey = objRegistry.OpenSubKey(strODBCRegKey + strRegKey);

            // check if there are any values in the Registry key
            if (objRegKey.ValueCount > 0)
            {

                // get the value names and data from the specified Registry key
                // add each and it's associated data, to the output string
                // each line should take the form "ValueName"="ValueData"
                foreach (string strValueName in objRegKey.GetValueNames())
                {
                    // add a double quote to the string
                    strRegData = strRegData + @"""";

                    // add the name of the Registry value to the string
                    strRegData = strRegData + strValueName;

                    // add a double quote, equals sign, and a double quote to the string
                    strRegData = strRegData + @"""=""";

                    // add the data from the Registry value to the string
                    // if there are any mapped drives, check for and replace them with UNC paths
                    // any \s need to be replaced with \\s
                    switch (ynMappedDrives)
                    {
                        case true:
                            strRegData = strRegData + ConvertToUNCPath(objRegKey.GetValue(strValueName).ToString(), arrMappedDrives).Replace(@"\", @"\\");
                            break;
                        case false:
                            strRegData = strRegData + objRegKey.GetValue(strValueName).ToString().Replace(@"\", @"\\");
                            break;
                    }

                    // add a double quote to the string, and end the line
                    strRegData = strRegData + @"""" + strNL;

                }

  
            }

            //add an extra line break for the end of this Key
            strRegData = strRegData + strNL;

            // check if there are any subkeys in the Registry key
            if (objRegKey.SubKeyCount > 0)
            {
                // recurse for every subkey
                foreach (string strSubKey in objRegKey.GetSubKeyNames())
                {
                    strRegData = strRegData + GetRegData(strRegKey + @"\" + strSubKey, objRegistry);
                }
            }

            // return the string that we have built
            return strRegData;
        }

        private ArrayList GetMappedDrives()
        {
            // get the list of mapped drives for the currently logged on user

            // for building the list of mapped drives
            ArrayList arrDrives = new ArrayList();
            
            // the WMI management object
            ManagementScope objWMI = new ManagementScope();

            // define the WMI query for the logical disk service
            // DriveType 4 is Network drives
            ObjectQuery objQuery = new ObjectQuery("SELECT * FROM Win32_LogicalDisk WHERE DriveType = 4");
            
            //execute the query
            ManagementObjectSearcher objSearcher = new ManagementObjectSearcher(objWMI,objQuery);
           
            // convert the query results to a collection of drives
            ManagementObjectCollection colDrives = objSearcher.Get();

            // add each drive returned to the array
            foreach (ManagementObject objDrive in colDrives )
            {
                arrDrives.Add(objDrive.GetPropertyValue("DeviceID"));
            }
   
            // if no mapped drives were found, add a null entry to the array
            if (colDrives.Count == 0)
            {
                arrDrives.Add("None found");
            }

            // return the array we have built
            return arrDrives;
        }

        private string ConvertToUNCPath(string strRegValue, ArrayList arrMappedDrives)
        {
            // checks a string for a mapped drive path and returns it
            // with the path changed to a UNC path

            // process each mapped drive in the given array
            for (int i = 0; i <= arrMappedDrives.Count - 1; i++)
            {
                //get the mapped drive that we are checking for
                string strMappedDrive = arrMappedDrives[i].ToString() + "\\";

                // examine the given Registry value
                switch (strRegValue.Contains(strMappedDrive))
                {
                    case true:
                        //get the UNC path of the mapped drive
                        string strUNCPath = GetUNCPath(strMappedDrive.Remove(2));

                        // ask the user if they want to use a UNC path instead
                        // prepare the message box
                        string strMessage = "The DSN you selected contains the following data:" + strNL +
                                             strRegValue + strNL + strNL +
                                             strMappedDrive + " is a mapped network drive." + strNL + strNL +
                                            "Would you like to replace it with the following UNC path?" +strNL +
                                            strRegValue.Replace(strMappedDrive,strUNCPath + @"\") + strNL + strNL +
                                            "This will only affect the exported file, your computer's settings will not change.";
                        string strCaption = "Mapped drive detected!";
			            MessageBoxButtons msgButtons = MessageBoxButtons.YesNo;
			            DialogResult dlgReplacePath;

			            // display the MessageBox
                        dlgReplacePath = MessageBox.Show(strMessage, strCaption, msgButtons);

			        // if the user clicked Yes, replace the path
                        if (dlgReplacePath == System.Windows.Forms.DialogResult.Yes)
                            return strRegValue.Replace(strMappedDrive, strUNCPath + @"\");
                        else break;
                    
                    case false:
                        break;
                }
            }
            // if nothing was found, or the user chose not to replace the path, return the input string
            return strRegValue;
        }

        private string GetUNCPath(string strMappedDrive)
        {
            // returns the UNC path for a given mapped drive

            // the WMI management object
            ManagementScope objWMI = new ManagementScope();

            // define the WMI query for the logical disk service
            // DriveType 4 is Network drives
            ObjectQuery objQuery = new ObjectQuery("SELECT * FROM Win32_LogicalDisk WHERE DeviceID = " +@"""" +strMappedDrive + @"""");
            
            //execute the query
            ManagementObjectSearcher objSearcher = new ManagementObjectSearcher(objWMI,objQuery);
           
            // convert the query results to a collection of drives
            ManagementObjectCollection colDrives = objSearcher.Get();

            // return the UNC path
            foreach (ManagementObject objDrive in colDrives)
            {
                return objDrive.GetPropertyValue("ProviderName").ToString();
            }

            // if no mapped drives were found, return the input
            return strMappedDrive;
           }

        public frmDSNExport()
        {
            InitializeComponent();
        }

        //event handlers

        private void Main_Load(object sender, EventArgs e)
        {
            // startup actions

            // get the user settings
            objUserSettings = new Properties.Settings();

            // query the Registry for System DSNs
            ArrayList arrSystemDSNList = GetDSNList(ConnectToRegistry("HKLM"));

            // query the Registry for User DSNs
            ArrayList arrUserDSNList = GetDSNList(ConnectToRegistry("HKCU"));

            // populate the list box
            // first the System DSNs
            for (int i = 0; i <= arrSystemDSNList.Count - 1; i++)
            {
                //ignore the null entry
                if (arrSystemDSNList[i].ToString() != "None Found")
                {
                    lstbxDSNList.Items.Add(arrSystemDSNList[i].ToString() + " [System DSN]");
                }
            }

            // now the User DSNs
            for (int i = 0; i <= arrUserDSNList.Count - 1; i++)
            {
                //ignore the null entry
                if (arrUserDSNList[i].ToString() != "None Found")
                {
                    lstbxDSNList.Items.Add(arrUserDSNList[i].ToString() + " [User DSN]");
                }
            }

            // if no items have been added, create a null entry
            if (lstbxDSNList.Items.Count == 0)
            {
                lstbxDSNList.Items.Add("No DSNs were found");
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // exits the application

            // close the form
            this.Close();
        }

        private void btnExport_Click(object sender, EventArgs e)
        {
            // Export the selected DSN

            // Determine which Registry hive the DSN is in
            string strRegHive = GetDSNRegHive(lstbxDSNList.SelectedItem.ToString());
            
            // Get the DSN name from the string
            string strDSN = lstbxDSNList.SelectedItem.ToString();

            // Remove the Registry hive name from the end
            switch (strRegHive)
            {
                case "HKLM":
                    strDSN = strDSN.Replace(" [System DSN]", "");
                    break;
                case "HKCU":
                    strDSN = strDSN.Replace(" [User DSN]", "");
                    break;
            }

            // Build the export string
            string strRegFileText = BuildExportString(strDSN, strRegHive);

            // ask the user where they wish to save the .reg file
            // get the default export location from application settings
            // set the DSN name as the default filename
            dlgSaveFile.InitialDirectory = objUserSettings.DefaultExportLocation.ToString();
            dlgSaveFile.FileName = strDSN;
            if (dlgSaveFile.ShowDialog() == DialogResult.OK)
            {
                // Write the .reg file
                System.IO.File.WriteAllText(dlgSaveFile.FileName, strRegFileText);

                // Feedback
                // prepare the message box
                string strMessage = "The DSN  " + strDSN + " has been exported to " + strNL + dlgSaveFile.FileName + ".";
                string strCaption = "Export complete";
                MessageBoxButtons msgButtons = MessageBoxButtons.OK;
                DialogResult dlgExportComplete;

                // display the MessageBox
                dlgExportComplete = MessageBox.Show(strMessage, strCaption, msgButtons);
            }
        }

       private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // shows information about the application

            // prepare the message box
            string strVersion = this.ProductVersion;
            string strAboutCaption = "About DSN Export-o-Matic 3000";
            string strAboutText = "DSN Export-o-Matic 3000" + strNL +
                                  "Version " + strVersion + strNL +
                                  "\u00a9  David Helps 2010" + strNL + strNL +
                                  "http://dsnexport.codeplex.com";
            DialogResult dlgAbout;

            // displaythe message box
            dlgAbout = MessageBox.Show(strAboutText, strAboutCaption, MessageBoxButtons.OK);
        }

       private void lstbxDSNList_SelectedValueChanged(object sender, EventArgs e)
       {
           // when the user has selected a DSN, enable the Export button
           btnExport.Enabled = true;
       }

    }
}
